package com.iteaj.iot.tools.db.rdbms;

import cn.hutool.db.DbUtil;
import com.iteaj.iot.FrameworkException;
import com.iteaj.iot.tools.db.DBManager;
import com.iteaj.iot.tools.db.DBMeta;
import com.iteaj.iot.tools.db.ParamValue;
import com.iteaj.iot.tools.db.RdbmsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

public class DefaultRdbmsSqlManager implements DBManager<RdbmsHandle> {

    private DataSource dataSource;
    private Map<String, DBMeta> tableNameAndMetaMap = new HashMap<>(8);
    private Map<Class<?>, DBMeta> entityClassAndMetaMap = new HashMap<>(8);
    protected Logger logger = LoggerFactory.getLogger(getClass());

    public DefaultRdbmsSqlManager(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public RdbmsMeta getDBMeta(String tableName) {
        return (RdbmsMeta) tableNameAndMetaMap.get(tableName);
    }

    public RdbmsMeta getDBMeta(Class entityClass) {
        DBMeta dbMeta = entityClassAndMetaMap.get(entityClass);
        if(dbMeta == null) {
            synchronized (this) {
                dbMeta = entityClassAndMetaMap.get(entityClass);
                if(dbMeta == null) {
                    dbMeta = new RdbmsMeta(entityClass);
                    entityClassAndMetaMap.put(entityClass, dbMeta);
                }
            }
        }

        return (RdbmsMeta) dbMeta;
    }

    @Override
    public RdbmsMeta remove(String tableName) {
        return (RdbmsMeta) tableNameAndMetaMap.get(tableName);
    }

    @Override
    public RdbmsMeta register(DBMeta meta) {
        if(meta instanceof RdbmsMeta) {
            return (RdbmsMeta) tableNameAndMetaMap.put(meta.getTableName(), meta);
        }

        throw new RdbmsException("只支持["+RdbmsMeta.class.getSimpleName()+"]类型对象");
    }

    @Override
    public RdbmsMeta registerIfAbsent(DBMeta meta) {
        if(!tableNameAndMetaMap.containsKey(meta.getTableName())) {
            return this.register(meta);
        }

        return null;
    }

    @Override
    public int insert(String tableName, Object entity) {
        RdbmsMeta dbMeta = this.getDBMeta(tableName);
        List<ParamValue> values = dbMeta.getParams(entity);

        return this.execUpdate(this.dataSource, dbMeta,1, values.toArray());
    }

    @Override
    public int insert(String tableName, Map<String, Object> value) {
        RdbmsMeta dbMeta = this.getDBMeta(tableName);
        List<ParamValue> values = dbMeta.getParams(value);

        return this.execUpdate(this.dataSource, dbMeta, 1, values.toArray());
    }

    @Override
    public int insert(Class entityClazz, Object entity) {
        RdbmsMeta dbMeta = this.getDBMeta(entityClazz);
        List<ParamValue> values = dbMeta.getParams(entity);

        return this.execUpdate(this.dataSource, dbMeta, 1, values.toArray());
    }

    @Override
    public int batchInsert(String tableName, List<Object> entities) {
        RdbmsMeta dbMeta = this.getDBMeta(tableName);
        List<Object[]> values = new ArrayList<>();
        for (Object entity : entities) {
            List<ParamValue> params = dbMeta.getParams(entity);
            values.add(params.toArray());
        }

        return execUpdate(this.dataSource, dbMeta, entities.size(), values);
    }

    @Override
    public int batchInsert(Class entityClazz, List<Object> entities) {
        RdbmsMeta dbMeta = this.getDBMeta(entityClazz);
        List<Object[]> values = new ArrayList<>();
        for (Object entity : entities) {
            values.add(dbMeta.getParams(entity).toArray());
        }

        return execUpdate(this.dataSource, dbMeta, entities.size(), values);
    }

    @Override
    public void execute(Object entity, RdbmsHandle handle) {
        RdbmsMeta dbMeta = this.getDBMeta(entity.getClass());
        List<ParamValue> values = dbMeta.getParams(entity);
        this.execUpdate(handle.rdbmsDataSource(entity), dbMeta, 1, values.toArray());
    }

    protected int execUpdate(DataSource dataSource, RdbmsMeta meta, int size, Object values) {
        long start = System.currentTimeMillis();
        String sqlStatement = meta.getStatement(size);
        try {
            try {
                if(size == 1) {
                    Object[] params = (Object[]) values;
                    return DbUtil.use(dataSource).execute(sqlStatement, params);
                } else {
                    DbUtil.use(dataSource).executeBatch(sqlStatement, (Iterable<Object[]>) values);
                }
            } catch (SQLException throwables) {
                throw new FrameworkException(throwables);
            }

            return size;
        } finally {
            if(logger.isTraceEnabled()) {
                StringBuilder sb = new StringBuilder();
//                for (ParamValue value : values) {
//                    sb.append(value.getFieldValue()).append(", ");
//                }

                logger.trace("Rdbms适配 数据入库({}ms) - 条数：{}\r\n\t    Sql：{} \r\n\t params：{}"
                        , System.currentTimeMillis() - start, size, sqlStatement, sb.substring(0, sb.length() - 2));
            }
        }

    }
}
